package com.orionlily.redis3t.breakdown;

import com.orionlily.redis3t.entity.User;
import com.orionlily.redis3t.service.UserService;
import com.orionlily.redis3t.throught.BloomFilterHelper;
import com.orionlily.redis3t.throught.RedisBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 缓存击穿(数据库存在它)
 *
 * 缓存击穿是我们可能遇到的第二个使用缓存方案可能遇到的问题。
 *
 * 在平常高并发的系统中，大量的请求同时查询一个 key 时，此时这个key正好失效了，
 * 就会导致大量的请求都打到数据库上面去。这种现象我们称为缓存击穿
 *
 * @author li.lc
 */
@RestController
@RequestMapping("/breakdown")
public class BreakdownController {

    @Resource(name = "redisTemplate")
    private RedisTemplate redisTemplate;

    @Resource(name = "userService")
    private UserService userService;

    private static final String mutex = "mutex";

    @Resource(name = "redissonClient")
    private RedissonClient redissonClient;

    @Resource(name = "bloomFilterHelper")
    private BloomFilterHelper bloomFilterHelper;

    @Resource(name = "redisBloomFilter")
    private RedisBloomFilter redisBloomFilter;

    /**
     * 互斥锁
     */
    @GetMapping("/{id}")
    public String getKey(@PathVariable Long id) throws InterruptedException {
        Object o = redisTemplate.opsForValue().get("llcT" + id.toString());
        if (Objects.isNull(o)) {
            if (redisTemplate.opsForValue().setIfAbsent(mutex, 1, 3 * 60, TimeUnit.SECONDS)) {
                System.out.println(Thread.currentThread() + " : 在缓存中找不到数据，拿到互斥锁，从数据库拿到数据并设置");
                User user = userService.queryById(id);
                redisTemplate.opsForValue().set("llcT" + id.toString(), user, 30, TimeUnit.MINUTES);
                redisTemplate.delete(mutex);
                return user.toString();
            } else {
                System.out.println(Thread.currentThread() + "在缓存中找不到数据，拿不到互斥锁，睡眠，递归");
                TimeUnit.MILLISECONDS.sleep(50);
                return getKey(id);
            }
        } else {
            System.out.println(Thread.currentThread() + "在缓存中找到数据了");
            return o.toString();
        }
    }

    /**
     * 使用redisson
     */
    @GetMapping("/redisson/{id}")
    public String getKeyWithRedisson(@PathVariable Long id) throws InterruptedException {
        Object o = redisTemplate.opsForValue().get("llcRedisson" + id.toString());
        if (Objects.isNull(o)) {
            RLock rLock = redissonClient.getLock("mutexid");
            boolean getJwtLockFlag = false;
            try {
                if (getJwtLockFlag = rLock.tryLock(0, 10, TimeUnit.SECONDS)) {
                    System.out.println(Thread.currentThread() + " : 在缓存中找不到数据，拿到redisson锁，从数据库拿到数据并设置");
                    User user = userService.queryById(id);
                    redisTemplate.opsForValue().set("llcRedisson" + id.toString(), user, 30, TimeUnit.MINUTES);
                    return user.toString();
                } else {
                    System.out.println(Thread.currentThread() + "在缓存中找不到数据，拿不到redisson锁，睡眠，递归");
                    TimeUnit.MILLISECONDS.sleep(50);
                    return getKey(id);
                }
            }finally {
                if (getJwtLockFlag) {
                    rLock.unlock();
                }
            }
        } else {
            System.out.println(Thread.currentThread() + "在缓存中找到数据了");
            return o.toString();
        }
    }

    /**
     * 集合
     */
    @GetMapping("/mix/{id}")
    public String getKeyByMix(@PathVariable Long id) throws InterruptedException {
        boolean inBloom = redisBloomFilter.includeByBloomFilter(bloomFilterHelper,"id_exist_bloom",id.toString());
        if (!inBloom){
            System.out.println("id="+id.toString() + "不在bloomFilter中");
        }

        Object o = redisTemplate.opsForValue().get("llcMix" + id.toString());
        if (Objects.isNull(o)) {
            if (redisTemplate.opsForValue().setIfAbsent(mutex, 1, 3 * 60, TimeUnit.SECONDS)) {
                System.out.println(Thread.currentThread() + " : 在缓存中找不到数据，拿到互斥锁，从数据库拿到数据并设置");
                User user = userService.queryById(id);
                redisTemplate.opsForValue().set("llcMix" + id.toString(), user, 30, TimeUnit.MINUTES);
                redisTemplate.delete(mutex);
                return user.toString();
            } else {
                System.out.println(Thread.currentThread() + "在缓存中找不到数据，拿不到互斥锁，睡眠，递归");
                TimeUnit.MILLISECONDS.sleep(50);
                return getKey(id);
            }
        } else {
            System.out.println(Thread.currentThread() + "在缓存中找到数据了");
            return o.toString();
        }
    }
}
